package cn.bjxy.shishaolong.system.user.service;

import cn.bjxy.shishaolong.common.StatusCode;
import cn.bjxy.shishaolong.common.SysConst;
import cn.bjxy.shishaolong.system.entity.Role;
import cn.bjxy.shishaolong.system.exception.OperatorFailureException;
import cn.bjxy.shishaolong.system.profession_course.entity.Profession;
import cn.bjxy.shishaolong.system.profession_course.entity.StudentProfessionRelation;
import cn.bjxy.shishaolong.system.profession_course.mapper.ProfessionMapper;
import cn.bjxy.shishaolong.system.profession_course.mapper.StudentProfessionRelationMapper;
import cn.bjxy.shishaolong.system.service.IResourcesService;
import cn.bjxy.shishaolong.system.service.IRoleService;
import cn.bjxy.shishaolong.system.user.entity.SysUserInfo;
import cn.bjxy.shishaolong.system.user.entity.SysUserLocalAuthen;
import cn.bjxy.shishaolong.system.user.mapper.SysUserInfoMapper;
import cn.bjxy.shishaolong.utils.SysUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 时少龙
 * @since 2019-03-09
 */
@Service
@PropertySource("classpath:config/system-config.properties")
public class SysUserInfoServiceImpl extends ServiceImpl<SysUserInfoMapper, SysUserInfo> implements ISysUserInfoService {

    @Autowired
    private SysUserInfoMapper userInfoMapper;
    @Autowired
    private ISysUserLocalAuthenService localAuthenService;
    @Autowired
    private ProfessionMapper professionMapper;
    @Autowired
    private IRoleService roleService;
    @Autowired
    private SysUtil sysUtil;

    @Override
    public IPage<SysUserInfo> queryByCondition(IPage<SysUserInfo> page, SysUserInfo userInfo) {
        List<SysUserInfo> userInfoList = userInfoMapper.queryByCondition(page, userInfo);
        return page.setRecords(userInfoList);
    }

    @Override
    public List<SysUserInfo> queryByCondition(SysUserInfo userInfo) {
        return userInfoMapper.queryByCondition(userInfo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean remove(String ids) {
        Boolean flag = false;
        for (String id : ids.split(",")) {
            // 对用户信息表进行逻辑删除
            flag = this.update(new UpdateWrapper<SysUserInfo>()
                    .set("deleted_at", LocalDateTime.now()).eq("tid", id));

            // 对本地用户认证表进行逻辑删除
            flag = localAuthenService.update(new UpdateWrapper<SysUserLocalAuthen>()
                    .set("deleted_at", LocalDateTime.now()).lambda().eq(SysUserLocalAuthen::getUserId, id));
        }
        return flag;
    }

    @Override
    public Map<String, Object> getSelectData(String roleKey) {
        Map<String, Object> map = new HashMap<>();
        // 查询出普通用户角色列表
        ArrayList<String> roleKeys = new ArrayList<>();
        List<Role> roleList = roleService.list();
        if (SysConst.ROLE_KEY_ADMIN.equals(roleKey)) {
            List<Role> commonRoleList = roleList.stream().filter(role -> SysConst.ROLE_KEY_TEACHER.equals(role.getKey())
                    || SysConst.ROLE_KEY_STUDENT.equals(role.getKey())).collect(Collectors.toList());
            // 查询出专业列表
            map.put(SysConst.RESULT_ROLE_LIST, commonRoleList);
        } else {
            List<Role> adminRoleList = roleList.stream().filter(role -> SysConst.ROLE_KEY_ADMIN.equals(role.getKey())
                    || SysConst.ROLE_KEY_SUPER_ADMIN.equals(role.getKey())).collect(Collectors.toList());
            map.put(SysConst.RESULT_ROLE_LIST, adminRoleList);
        }
        List<Profession> professionList = professionMapper.selectList(null);
        map.put(SysConst.RESULT_PROFESSION_LIST, professionList);
        return map;
    }

    @Autowired
    private IResourcesService resourcesService;

    @Override
    public SysUserInfo getUserInfoById(Integer userId) {
        SysUserInfo userInfo = userInfoMapper.getUserInfoById(userId);
        List<String> accessList = resourcesService.queryResourcesKeysByUserId(userId);
        userInfo.setAccess(accessList);
        return userInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysUserInfo register(SysUserInfo sysUserInfo) {
        if (this.save(sysUserInfo)) {
            return sysUserInfo;
        } else {
            return null;
        }
    }

    @Override
    public void updatePassword(SysUserLocalAuthen authen) {
        // todo 密码验证，常规验证
        checkPassword(authen.getPassword());
        //修改密码
        authen.setPassword(sysUtil.encodePassWord(authen));// 对密码进行加密处理
        authen.setUpdateAt(LocalDateTime.now());
        authen.setNo(null);
        boolean update = localAuthenService.update(authen, new UpdateWrapper<SysUserLocalAuthen>().lambda()
                .eq(SysUserLocalAuthen::getUserId, authen.getUserId()));
        if (!update)
            throw new OperatorFailureException(StatusCode.UPDATE_FAILURE, StatusCode.UPDATE_FAILURE_MSG);
    }

    /**
     * todo 定义密码验证规则
     *
     * @param password
     */
    private void checkPassword(String password) {

    }

    @Override
    public Boolean checkOldPassword(SysUserLocalAuthen authen) {
        //验证旧密码
        authen.setPassword(authen.getOldPassword()); // 给密码赋值，用于加密
        SysUserLocalAuthen resultAuthen = localAuthenService.getOne(new QueryWrapper<SysUserLocalAuthen>().lambda()
                .eq(SysUserLocalAuthen::getUserId, authen.getUserId())
                .eq(SysUserLocalAuthen::getPassword, sysUtil.encodePassWord(authen)));
        return resultAuthen == null ? false : true;
    }


    @Autowired
    private StudentProfessionRelationMapper relationMapper;
    @Value("${user.defaultPassword}")
    @Setter
    private String defaultPassword;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(SysUserInfo userInfo) {
        userInfo.setUpdateAt(LocalDateTime.now());
        if (userInfo.getTid() == null) {  // 新建
            boolean flag = true;
            // 1.生成默认密码，保存用户拿到用户的tid
            if (userInfoMapper.insert(userInfo) <= 0) flag = false; // 插入失败
            //2.根据不同用户身份生成不同的工号，保存到认证表中
            SysUserLocalAuthen localAuthen = new SysUserLocalAuthen();
            localAuthen.setUserId(userInfo.getTid()).setNo(getNo(userInfo))
                    .setPhone(userInfo.getPhone()).setPassword(userInfo.getPassword()); // 设置密码
            // 设置初始密码
            if (StringUtils.isEmpty(localAuthen.getPassword())) localAuthen.setPassword(defaultPassword);
            String encodePassWord = sysUtil.encodePassWord(localAuthen); // 对密码进行加密
            localAuthen.setPassword(encodePassWord);
            flag = localAuthenService.save(localAuthen);
            userInfo.setNo(localAuthen.getNo());

            //3.如果是学生，插入与专业的关联关系
            if (roleService.judgeRole(userInfo.getRoleId(), SysConst.ROLE_KEY_STUDENT)) {
                StudentProfessionRelation relation = new StudentProfessionRelation();
                relation.setUserId(userInfo.getTid()).setProfessionId(userInfo.getProfessionId());
                if (relationMapper.insert(relation) <= 0) flag = false;
            }
            return flag;
        } else {  // 修改
            userInfoMapper.updateById(userInfo);
            localAuthenService.update(new UpdateWrapper<SysUserLocalAuthen>().lambda()
                    .set(SysUserLocalAuthen::getPhone, userInfo.getPhone())
                    .eq(SysUserLocalAuthen::getUserId, userInfo.getTid()));
//             若学生，修改与专业之间的关联关系
            if (roleService.judgeRole(userInfo.getRoleId(), SysConst.ROLE_KEY_STUDENT)) {
                if (userInfo.getProfessionId() != null) {  // 学生不允许修改自己的专业
                    StudentProfessionRelation relation = relationMapper.selectOne(
                            new QueryWrapper<StudentProfessionRelation>().lambda()
                                    .eq(StudentProfessionRelation::getUserId, userInfo.getTid())
                                    .eq(StudentProfessionRelation::getTid, userInfo.getProfessionId()));
                    if (Objects.isNull(relation)) { // 说明修改了
                        StudentProfessionRelation relationTemp = new StudentProfessionRelation();
                        relationTemp.setDeletedAt(LocalDateTime.now()).setUpdateAt(LocalDateTime.now());
                        //删除原先的
                        relationMapper.update(relationTemp, new UpdateWrapper<StudentProfessionRelation>().lambda()
                                .eq(StudentProfessionRelation::getUserId, userInfo.getTid()));
                        // 新建对应关系
                        relationTemp.setDeletedAt(null).setUserId(userInfo.getTid())
                                .setProfessionId(userInfo.getProfessionId());
                        relationMapper.insert(relationTemp);
                    }
                }
            }
            // 若老师，判断是否存在到专业的关联关系，若有，删除。  角色不允许变更
/*            if (roleService.judgeRole(userInfo.getRoleId(), SysConst.ROLE_KEY_TEACHER)) {
                relationMapper.delete(new QueryWrapper<StudentProfessionRelation>().lambda()
                        .eq(StudentProfessionRelation::getUserId, userInfo.getTid()));
            }*/
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean wxSave(SysUserInfo userInfo) {
        return super.saveOrUpdate(userInfo);
    }

    /**
     * 获取工号
     *
     * @param userInfo
     * @return
     */
    private String getNo(SysUserInfo userInfo) {
        Integer roleId = userInfo.getRoleId();
        isEmpty(roleId);
        if (roleService.judgeRole(roleId, SysConst.ROLE_KEY_ADMIN, SysConst.ROLE_KEY_SUPER_ADMIN)) { // 管理员 66+4位
            return generatorNo(roleId, SysConst.ADMIN_WORK_NO_PREFIX + "%04d");
        } else if (roleService.judgeRole(roleId, SysConst.ROLE_KEY_TEACHER)) { // 老师 88+4位
            return generatorNo(roleId, SysConst.TEACHER_WORK_NO_PREFIX + "%04d");
        } else if (roleService.judgeRole(roleId, SysConst.ROLE_KEY_STUDENT)) { // 学生
            return generateStudentNo(userInfo);
        } else {
            throw new OperatorFailureException(StatusCode.ILLEGAL_OPERATION, StatusCode.ILLEGAL_OPERATION_MESSAGE);
        }
    }

    /**
     * 生成学生的工号
     *
     * @param userInfo
     * @return
     */
    private String generateStudentNo(SysUserInfo userInfo) {
        StringBuilder studentNoFormat = new StringBuilder();
        int year = LocalDate.now().getYear();
        studentNoFormat.append(year); // 学年
        studentNoFormat.append("%03d"); // 专业
        studentNoFormat.append("%04d");
        Integer count = relationMapper.selectCount(new QueryWrapper<StudentProfessionRelation>().lambda()
                .eq(StudentProfessionRelation::getProfessionId, userInfo.getProfessionId())) + 1;
        return new Formatter().format(studentNoFormat.toString(), userInfo.getProfessionId(), count).toString();
    }

    /**
     * 生成不同角色用户的工号
     *
     * @param roleId
     * @return
     */
    private String generatorNo(Integer roleId, String formatStr) {
        long count = getRoleCount(roleId);
        Formatter formatter = new Formatter();
        return formatter.format(formatStr, count).toString();
    }

    /**
     * 获取指定角色在用户表中的数量
     *
     * @param roleId
     * @return
     */
    private long getRoleCount(Integer roleId) {
        List<SysUserInfo> sysUserInfos = userInfoMapper.selectList(new QueryWrapper<SysUserInfo>().lambda().eq(SysUserInfo::getRoleId, roleId));
        if (sysUserInfos != null) {
            return sysUserInfos.size() + 1;
        } else {
            // todo 记录错误信息，写入日志
            throw new OperatorFailureException(StatusCode.SERVER_INNER_ERROR, StatusCode.SERVER_INNER_ERROR_MSG);
        }
    }

    private void isEmpty(Object object) {
        if (StringUtils.isEmpty(object)) {
            throw new OperatorFailureException(StatusCode.INFO_ERROR, StatusCode.INFO_ERROR_MSG);
        }
    }
}
